#include "address.h"

#include <bit> // std::bit_cast
#include <algorithm> // std::max

#include <netdb.h> // addrinfo getaddrinfo freeaddrinfo EAI_*
#include <arpa/inet.h> // inet_ntop INET_ADDRSTRLEN INET6_ADDRSTRLEN

#include "handle_common.hpp" // custom_deleter

namespace simple::io
{
	free_address_config::handle_t free_address_config::null() noexcept
	{ return std::bit_cast<free_address_config::handle_t>(nullptr); }

	void free_address_config::deleter(handle_t h) noexcept
	{
		freeaddrinfo(std::bit_cast<addrinfo*>(h));
	}

	template class custom_deleter<free_address_config>;

	address_result make_address(const std::string& name, const std::string& service) noexcept
	{
		addrinfo* ret;
		auto code = getaddrinfo(name.c_str(), service.c_str(), nullptr, &ret);
		if(0 == code) return address(std::bit_cast<free_address_config::handle_t>(ret));
		else switch(code)
		{
			case EAI_AGAIN: return address_try_again{errno, code};
			case EAI_NONAME: return name_unresolved{errno, code};
			case EAI_SERVICE: return service_unrecognized{errno, code};
			default: return address_error{errno, code};
		}
	}

	std::string to_string(const address& addr)
	{
		std::string result;
		result.resize(std::max(INET_ADDRSTRLEN, INET6_ADDRSTRLEN));
		auto raw_addr = std::bit_cast<addrinfo*>(addr.get());
		inet_ntop(raw_addr->ai_addr->sa_family, raw_addr->ai_addr->sa_data, result.data(), result.size());
		return result;
	}

} // namespace simple::io
